[Notes] AJAX Basic

What is AJAX?

AJAX is not a programming language.
AJAX just uses a combination of:

  • A browser built-in XMLHttpRequest object (to request data from a web server)
  • JavaScript and HTML DOM (to display or use the data)

AJAX 可以实现异步通信

How AJAX Works

13. AJAX Basic-2019-4-17-13-40-54.png

How to use AJAX

The keystone of AJAX is the XMLHttpRequest object.

creating an XMLHttpRequest object

1
var xhttp = new XMLHttpRequest();

send a request to a server, we use the open() and send() methods of the XMLHttpRequest object

1
2
xhttp.open("GET", "ajax_info.txt", true);
xhttp.send();

13. AJAX Basic-2019-4-17-13-46-10.png

GET or POST?

GET is simpler and faster than POST, and can be used in most cases.
However, always use POST requests when:

  • A cached file is not an option (update a file or database on the server).
  • Sending a large amount of data to the server (POST has no size limitations).
  • Sending user input (which can contain unknown characters), POST is more robust and secure than GET.

Project

demo of our web applications: http://34.211.21.63/Event/

Complete HTML/CSS/Javascript code

http://jsbin.com/hocukukiva/edit?html,css,js

将JSBin里面的代码分别放在:

1
2
3
/WebContent/index.html
/WebContent/styles/main.css
/WebContent/scripts/main.js

访问: http://localhost:8080/Jupiter

自动跳转至index.html
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset

1
2
3
4
5
6
7
font-size: 0.9em; 
/* means 0.9 * parent's font-size */

.top-nav {
/* flex means listed from left to right */
flex: 1;
}

JavaScript

Step 1 ()() Run the scripts

1
2
3
4
// Run all following scripts
(function() {

})()

Step 2 Gloval Variables

1
2
3
4
var user_id = '1111';
var user_fullname = 'John Smith';
var lng = -122.08;
var lat = 37.38;

step3: main function(entrance)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
init();
/* step4: define init function */
function init() {
// Register event listeners
$('nearby-btn').addEventListener('click', loadNearbyItems);
// $('fav-btn').addEventListener('click', loadFavoriteItems);
// $('recommend-btn').addEventListener('click', loadRecommendedItems);

var welcomeMsg = $('welcome-msg');
welcomeMsg.innerHTML = 'Welcome, ' + user_fullname;

// step 7
initGeoLocation();
}

step5: create $ function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* A helper function that creates a DOM element <tag options...>
*/
function $(tag, options) {
if (!options) {
return document.getElementById(tag);
}
var element = document.createElement(tag);

for ( var option in options) {
if (options.hasOwnProperty(option)) {
element[option] = options[option];
}
}
return element;
}

step6: create AJAX helper function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* @param method - GET|POST|PUT|DELETE
* @param url - API end point
* @param callback - This the successful callback
* @param errorHandler - This is the failed callback
*/
function ajax(method, url, data, callback, errorHandler) {
var xhr = new XMLHttpRequest();

xhr.open(method, url, true);

// 判断request
xhr.onload = function() {
// 判断 resopnse
if (xhr.status === 200) {
// callback 属于回调函数
callback(xhr.responseText);
} else if (xhr.status === 403) {
onSessionInvalid();
} else {
errorHandler();
}
};

xhr.onerror = function() {
console.error("The request couldn't be completed.");
errorHandler();
};

if (data === null) {
xhr.send();
} else {
xhr.setRequestHeader("Content-Type",
"application/json;charset=utf-8");
xhr.send(data);
}
}

step 7: initGeoLocation function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function initGeoLocation() {
if (navigator.geolocation) {
// step 8
// get current position by browser.
navigator.geolocation.getCurrentPosition(onPositionUpdated,
onLoadPositionFailed, {
maximumAge : 60000
});
showLoadingMessage('Retrieving your location...');
} else {
// step 9
onLoadPositionFailed();
}
}

step 8: onPositionUpdated function

1
2
3
4
5
6
7
function onPositionUpdated(position) {
lat = position.coords.latitude;
lng = position.coords.longitude;

// step 11
loadNearbyItems();
}

step 9: onLoadPositionFailed function

1
2
3
4
5
6
function onLoadPositionFailed() {
console.warn('navigator.geolocation is not available');

//step 10
getLocationFromIP();
}

step 10: getLocationFromIP function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getLocationFromIP() {
// Get location from http://ipinfo.io/json
var url = 'http://ipinfo.io/json'
var req = null;
ajax('GET', url, req, function(res) {
var result = JSON.parse(res);
if ('loc' in result) {
var loc = result.loc.split(',');
lat = loc[0];
lng = loc[1];
} else {
console.warn('Getting location by IP failed.');
}
// step 11
loadNearbyItems();
});
}

step 11: loadNearbyItems function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* API #1 Load the nearby items API end point: [GET]
* /Dashi/search?user_id=1111&lat=37.38&lon=-122.08
*/
function loadNearbyItems() {
console.log('loadNearbyItems');
// step 12
activeBtn('nearby-btn');

// The request parameters
var url = './search';
var params = 'user_id=' + user_id + '&lat=' + lat + '&lon=' + lng;
var req = JSON.stringify({});

// step 13
// display loading message
showLoadingMessage('Loading nearby items...');

// make AJAX call
ajax('GET', url + '?' + params, req,
// successful callback
function(res) {
var items = JSON.parse(res);
if (!items || items.length === 0) {
// step 14
showWarningMessage('No nearby item.');
} else {
// step 16
listItems(items);
}
},
// failed callback
function() {
// step 15
showErrorMessage('Cannot load nearby items.');
});
}

step 12: activeBtn function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* A helper function that makes a navigation button active
*
* @param btnId - The id of the navigation button
*/
function activeBtn(btnId) {
var btns = document.getElementsByClassName('main-nav-btn');

// deactivate all navigation buttons
for (var i = 0; i < btns.length; i++) {
btns[i].className = btns[i].className.replace(/\bactive\b/, '');
}

// active the one that has id = btnId
var btn = $(btnId);
btn.className += ' active';
}

step 13: showLoadingMessage function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function showLoadingMessage(msg) {
var itemList = $('item-list');
itemList.innerHTML = '<p class="notice"><i class="fa fa-spinner fa-spin"></i> '
+ msg + '</p>';
}

/** step 14: showWarningMessage function **/
function showWarningMessage(msg) {
var itemList = $('item-list');
itemList.innerHTML = '<p class="notice"><i class="fa fa-exclamation-triangle"></i> '
+ msg + '</p>';
}

/** step15: showErrorMessage function **/
function showErrorMessage(msg) {
var itemList = $('item-list');
itemList.innerHTML = '<p class="notice"><i class="fa fa-exclamation-circle"></i> '
+ msg + '</p>';
}

step16: listItems function

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @param items - An array of item JSON objects
*/
function listItems(items) {
// Clear the current results
var itemList = $('item-list');
itemList.innerHTML = '';

for (var i = 0; i < items.length; i++) {
// step 17
addItem(itemList, items[i]);
}
}

step17: addItem function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
* Add item to the list
* @param itemList - The <ul id="item-list"> tag
* @param item - The item data (JSON object)
*/
function addItem(itemList, item) {
var item_id = item.item_id;

// create the <li> tag and specify the id and class attributes
var li = $('li', {
id : 'item-' + item_id,
className : 'item'
});

// set the data attribute
li.dataset.item_id = item_id;
li.dataset.favorite = item.favorite;

// item image
if (item.image_url) {
li.appendChild($('img', {
src : item.image_url
}));
} else {
li.appendChild($('img', {
src : 'https://assets-cdn.github.com/images/modules/logos_page/GitHub-Mark.png'
}))
}
// section
var section = $('div', {});

// title
var title = $('a', {
href : item.url,
target : '_blank',
className : 'item-name'
});
title.innerHTML = item.name;
section.appendChild(title);

// category
var category = $('p', {
className : 'item-category'
});
category.innerHTML = 'Category: ' + item.categories.join(', ');
section.appendChild(category);

var stars = $('div', {
className : 'stars'
});

for (var i = 0; i < item.rating; i++) {
var star = $('i', {
className : 'fa fa-star'
});
stars.appendChild(star);
}

if (('' + item.rating).match(/\.5$/)) {
stars.appendChild($('i', {
className : 'fa fa-star-half-o'
}));
}

section.appendChild(stars);

li.appendChild(section);

// address
var address = $('p', {
className : 'item-address'
});

address.innerHTML = item.address.replace(/,/g, '<br/>').replace(/\"/g,
'');
li.appendChild(address);

// favorite link
var favLink = $('p', {
className : 'fav-link'
});

favLink.onclick = function() {
changeFavoriteItem(item_id);
};

favLink.appendChild($('i', {
id : 'fav-icon-' + item_id,
className : item.favorite ? 'fa fa-heart' : 'fa fa-heart-o'
}));

li.appendChild(favLink);

itemList.appendChild(li);
}